package cn.mdmbct.douyin.common.executor;

import cn.mdmbct.douyin.common.proxy.ParamCache;
import cn.mdmbct.douyin.common.proxy.annotations.RequestBody;
import cn.mdmbct.douyin.common.proxy.annotations.RequestParam;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
 * Dy Service Interface方法解析器
 *
 * @author mdmbct  mdmbct@outlook.com
 * @date 5/12/2021 6:03 PM
 * @modified mdmbct
 * @since 1.0
 */
@Slf4j
public class MethodParser {

    private final Method method;

    private final ParamCache paramCache;

    public MethodParser(Method method, ParamCache paramCache) {
        this.method = method;
        this.paramCache = paramCache;
    }

    /**
     * 解析method上参数列表中标有{@link cn.mdmbct.douyin.common.proxy.annotations.RequestParam}的参数名
     * @return 抖音open api请求url上拼接的参数名列表
     */
    private List<UrlParam> parseRequestParam(Object... args) {
        Parameter[] parameters = method.getParameters();

        List<UrlParam> urlParams = new ArrayList<>(parameters.length);

        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];

            // 在url中的参数名
            String urlParamName = paramCache.get(parameter.getName());
            RequestParam annotation = parameter.getAnnotation(RequestParam.class);

            // 解析name
            if (urlParamName == null) {
                // RequestParam注解中value是否为""
                if ("".equals(annotation.value())) {
                    urlParamName = parameter.getName();
                } else {
                    urlParamName = annotation.value();
                }
                // 记得放入缓存 下次直接获取
                paramCache.add(parameter.getName(), urlParamName);
            }

            // 解析value
            if ("".equals(annotation.method())) {
                urlParams.add(new UrlParam(urlParamName, String.valueOf(args[i])));
            } else {
                try {
                    Method declaredMethod = args[i].getClass().getDeclaredMethod(annotation.method());
                    Object value = declaredMethod.invoke(args[i]);
                    urlParams.add(new UrlParam(urlParamName, String.valueOf(value)));
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                    log.error("MethodParser#parseRequestParam " + "Parse Request Param Failed: " + e.getCause());
                }

            }
        }
        return urlParams;
    }

    private Object parseRequestBody(Object... args) {
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            if (parameter.getAnnotation(RequestBody.class) != null) {
                return args[i];
            }
        }
        return null;
    }

    public Request parse(Object... args) {
        return new Request(
                parseRequestParam(args),
                parseRequestBody(args)
        );
    }



    @Getter
    private static class UrlParam {

        private final String name;

        private final String value;

        public UrlParam(String name, String value) {
            this.name = name;
            this.value = value;
        }
    }

    @Getter
    public static class Request {

        private final List<UrlParam> requestParams;

        private final Object requestBody;

        public Request(List<UrlParam> requestParams, Object requestBody) {
            this.requestParams = requestParams;
            this.requestBody = requestBody;
        }
    }
}
